#pragma once

#include "Util/Include/XMemMgr.h"

//////////////////////////////////////////////////////////////////////////
class ExportXSkinLib IXBaseObj;



//////////////////////////////////////////////////////////////////////////
#define DEF_IID_FROM_STRING(strguid, guid)   (IIDFromString(strguid, &guid), guid)
#define DEF_INTERFACE(Intf, id)                                         \
    public:                                                             \
    static GUID& GetIntfId()                                            \
    {                                                                   \
        static GUID Intf##guid = DEF_IID_FROM_STRING(id, Intf##guid);   \
        return Intf##guid;                                              \
    }

#define XINTFID(intf)     intf::GetIntfId()


//////////////////////////////////////////////////////////////////////////

typedef HRESULT (*ObjCreateFunc)(IN REFIID intfIID, OUT IXBaseObj** pResult);
typedef void*   (*IntfConvertFunc)(IN void* pSrcObj);

template< class T >
__inline HRESULT defIntfCreator(IN REFIID intfIID, OUT IXBaseObj** pResult)
{
    HRESULT hResult = E_NOINTERFACE;
    *pResult = NULL;
    T* pObj = XAlloc(T);
    pObj->OnConstruct();
    if(pObj != NULL)
    {
        hResult = pObj->QueryInterface(intfIID, (void**)pResult);
    }
    return hResult;
}

template < class T >
__inline void* defIntfConvertFunc(IN void* pSrcObj)
{
    T* pObj = reinterpret_cast<T*>(pSrcObj);
    return reinterpret_cast<void*>(pObj);
}

struct stClassEntry
{
    LPCTSTR         strClassName;
    GUID            clsid;
    ObjCreateFunc   pfnCreator;
};

enum eIntfFlags
{
    eIntf_Singleton     = 0x1,  // 
    eIntf_NoCreate      = 0x2,  // Disallowed to create out of XSkin
};

struct stIntfEntry
{
    GUID            intfGuid;
    IntfConvertFunc pfnConvertor;
    LPCTSTR         strIntfName;
    INT             nFlags;
};

ExportXSkinLib HRESULT defRegisterSkinClass(const stIntfEntry* const intfEntry, const stClassEntry* const clsEntry);
ExportXSkinLib HRESULT defQueryInterface(IXBaseObj* pObject, stIntfEntry* pEntry, REFIID riid, void **ppvObject);

#define BEGIN_DEF_SKIN_CLASS(classname, parentclass, id)  \
            public:                              \
            static stClassEntry* GetSkinClassInfo() \
            {                                   \
                static stClassEntry entry = {X_WIDEN_STR_EX(classname), DEF_IID_FROM_STRING(id, entry.clsid), &defIntfCreator<classname>};    \
                return &entry;                  \
            }                                   \
            virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) \
            {   \
                HRESULT hResult = parentclass::QueryInterface(riid, ppvObject);  \
                if(FAILED(hResult)) \
                {   \
                    hResult = defQueryInterface(dynamic_cast<IXBaseObj*>(this), GetSkinIntfInfo(), riid, ppvObject);  \
                }   \
                return hResult; \
            }   \
            static stIntfEntry*  GetSkinIntfInfo()  \
            {                                   \
                static stIntfEntry entry[] =    \
            {                               \
                {XINTFID(IXBaseObj), defIntfConvertFunc<IXBaseObj>,  X_WIDEN_STR_EX(IXBaseObj), eIntf_NoCreate},

#define IMP_BASIC_INTF(intf, flags)  \
                {XINTFID(intf), (IntfConvertFunc)defIntfConvertFunc<intf>,  X_WIDEN_STR_EX(intf), flags   },

#define END_DEF_SKIN_CLASS()                        \
                {GUID_NULL, NULL, NULL, 0   },      \
            };                                      \
            return entry;                           \
        };

#define IMPLEMENT_SKIN_CLASS(classname) \
        static HRESULT h##classname##Result = defRegisterSkinClass(classname::GetSkinIntfInfo(), classname::GetSkinClassInfo());

#define DEF_SKIN_CLASS_SINGLE_INTF(classname, parentclass, id, intf, flags)  \
    BEGIN_DEF_SKIN_CLASS(classname, parentclass, id)                         \
    IMP_BASIC_INTF(intf, flags)                               \
    END_DEF_SKIN_CLASS();

#define IMPL_CONTROL_OBJ(impl_class_name, base_impl_class_name, readable_name)          \
    virtual HRESULT STDMETHODCALLTYPE   GetControlName(OUT BSTR* pbstrName)             \
    {                                                                                   \
        return (::SysReAllocString(pbstrName, readable_name) != FALSE) ? S_OK : E_FAIL; \
    }

ExportXSkinLib extern const GUID   gs_GuidXSkin;
ExportXSkinLib extern LPOLESTR     gs_strGuidXSkin;


template < class Intf, class T >
BOOL XIsTypeOf(T* pObject)
{
    Intf* pIntf = NULL;
    if(SUCCEEDED(pObject->QueryInterface(XINTFID(Intf), (void**)&pIntf)))
    {
        pIntf->Release();
        return TRUE;
    }
    return FALSE;
}